package fr.teuteuf.runstickmanrun.entity;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.Array;

import fr.teuteuf.runstickmanrun.Animation;
import fr.teuteuf.runstickmanrun.Constants;

public class Player extends AbstractCharacter {

	public static final int INDEX_STANDING_ANIMATION = 0;
	public static final int INDEX_WALKING_ANIMATION  = 1;
	public static final int INDEX_RUNNING_ANIMATION  = 2;
	public static final int INDEX_JUMP_ANIMATION     = 3;
	public static final int INDEX_FALL_ANIMATION     = 4;
	public static final int INDEX_DYING_ANIMATION    = 5;

	private static final int NB_ANIMATIONS = 6;
	private static final int FRAME_WIDTH  = 64;
	private static final int FRAME_HEIGHT = 64;
	
	private static final int SPRITE_WIDTH  = 12;
	private static final int SPRITE_HEIGHT = 64;
	
	private static final int BAR_WIDTH   = 40;
	private static final int BAR_HEIGHT  = 5;
	
	private static Array<Texture> textures;
	private static Texture whiteTexture;
	
	private int textureIndex;
	private String name = new String();
	
	public Player(World world, int x, int y, float healthRegen, float energyRegen, int textureIndex) {
		maxHealth = health = 100;
		maxEnergy = energy = 100;
		jumpForce = 42f;
		movingSpeed = 4f;
		percentAcceleration = 50;
		this.textureIndex = textureIndex;
		this.healthRegen = healthRegen;
		this.energyRegen = energyRegen;
		
		initAnimations();
		currentAnimation = animations[INDEX_STANDING_ANIMATION];
		
		initBody(world, x, y);
	}
	
	public Player(World world, int x, int y, float healthRegen, float energyRegen, int textureIndex, String name) {
		this(world, x, y, healthRegen, energyRegen, textureIndex);
		this.name = name;
	}
	
	@Override
	protected void initAnimations() {
		animations = new Animation[NB_ANIMATIONS];
		
		Animation tempAnimation;
		
		//Standing animation
		tempAnimation = new Animation();
		tempAnimation.addFrame(textures.get(textureIndex), 0 * FRAME_WIDTH, 8 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 1);
		animations[INDEX_STANDING_ANIMATION] = tempAnimation;
		
		//Walking animation
		tempAnimation = new Animation();
		tempAnimation.addFrame(textures.get(textureIndex), 0 * FRAME_WIDTH, 4 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		tempAnimation.addFrame(textures.get(textureIndex), 1 * FRAME_WIDTH, 4 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		tempAnimation.addFrame(textures.get(textureIndex), 2 * FRAME_WIDTH, 4 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		tempAnimation.addFrame(textures.get(textureIndex), 3 * FRAME_WIDTH, 4 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		tempAnimation.addFrame(textures.get(textureIndex), 4 * FRAME_WIDTH, 4 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		tempAnimation.addFrame(textures.get(textureIndex), 5 * FRAME_WIDTH, 4 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		tempAnimation.addFrame(textures.get(textureIndex), 6 * FRAME_WIDTH, 4 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		tempAnimation.addFrame(textures.get(textureIndex), 7 * FRAME_WIDTH, 4 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		animations[INDEX_WALKING_ANIMATION] = tempAnimation;
		
		//Running animation
		tempAnimation = new Animation();
		tempAnimation.addFrame(textures.get(textureIndex), 4 * FRAME_WIDTH, 0 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.1f);
		tempAnimation.addFrame(textures.get(textureIndex), 5 * FRAME_WIDTH, 0 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.1f);
		tempAnimation.addFrame(textures.get(textureIndex), 6 * FRAME_WIDTH, 0 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.1f);
		tempAnimation.addFrame(textures.get(textureIndex), 7 * FRAME_WIDTH, 0 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.1f);
		tempAnimation.addFrame(textures.get(textureIndex), 0 * FRAME_WIDTH, 1 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.1f);
		tempAnimation.addFrame(textures.get(textureIndex), 1 * FRAME_WIDTH, 1 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.1f);
		tempAnimation.addFrame(textures.get(textureIndex), 2 * FRAME_WIDTH, 1 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.1f);
		tempAnimation.addFrame(textures.get(textureIndex), 3 * FRAME_WIDTH, 1 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.1f);
		animations[INDEX_RUNNING_ANIMATION] = tempAnimation;
		
		//Jump animation
		tempAnimation = new Animation(false);
		tempAnimation.addFrame(textures.get(textureIndex), 3 * FRAME_WIDTH, 5 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		tempAnimation.addFrame(textures.get(textureIndex), 4 * FRAME_WIDTH, 5 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		tempAnimation.addFrame(textures.get(textureIndex), 5 * FRAME_WIDTH, 5 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		animations[INDEX_JUMP_ANIMATION] = tempAnimation;
		
		//Fall animation
		tempAnimation = new Animation(false);
		tempAnimation.addFrame(textures.get(textureIndex), 6 * FRAME_WIDTH, 5 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		tempAnimation.addFrame(textures.get(textureIndex), 7 * FRAME_WIDTH, 5 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		animations[INDEX_FALL_ANIMATION] = tempAnimation;
		
		//Dying animation
		tempAnimation = new Animation(false);
		tempAnimation.addFrame(textures.get(textureIndex), 2 * FRAME_WIDTH, 2 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		tempAnimation.addFrame(textures.get(textureIndex), 3 * FRAME_WIDTH, 2 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		tempAnimation.addFrame(textures.get(textureIndex), 4 * FRAME_WIDTH, 2 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		tempAnimation.addFrame(textures.get(textureIndex), 5 * FRAME_WIDTH, 2 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		tempAnimation.addFrame(textures.get(textureIndex), 6 * FRAME_WIDTH, 2 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		tempAnimation.addFrame(textures.get(textureIndex), 7 * FRAME_WIDTH, 2 * FRAME_HEIGHT, FRAME_WIDTH, FRAME_HEIGHT, 0.07f);
		animations[INDEX_DYING_ANIMATION] = tempAnimation;
	}
	
	@Override
	protected void initBody(World world, int x, int y) {
		BodyDef bodyDef = new BodyDef();
		bodyDef.type = BodyType.DynamicBody;
		bodyDef.position.set(new Vector2((x + SPRITE_WIDTH/2) * Constants.WORLD_TO_BOX, (y + SPRITE_HEIGHT/2) * Constants.WORLD_TO_BOX));
		bodyDef.fixedRotation = true;
		
		PolygonShape polygonShape = new PolygonShape();
		polygonShape.setAsBox(SPRITE_WIDTH/2 * Constants.WORLD_TO_BOX, SPRITE_HEIGHT/2 * Constants.WORLD_TO_BOX);
		
		FixtureDef fixtureDef = new FixtureDef();
		fixtureDef.shape = polygonShape;
		fixtureDef.density = 1f; 
		fixtureDef.friction = 0f;
		fixtureDef.restitution = 0f;
		
		body = world.createBody(bodyDef);
		body.createFixture(fixtureDef);
		body.setUserData(this);
		polygonShape.dispose();
	}

	@Override
	public void updateCurrentAnimation() {
		Animation updatedCurrentAnimation = null;
		if(isDead()) {
			updatedCurrentAnimation = animations[Player.INDEX_DYING_ANIMATION];
		} else if(Math.abs(velocity.x) <= 0.01f && Math.abs(velocity.y) <= 0.01f) {
			updatedCurrentAnimation = animations[Player.INDEX_STANDING_ANIMATION];
		} else if(velocity.x != 0f && Math.abs(velocity.y) <= 0.01f && ! isSprinting()) {
			updatedCurrentAnimation = animations[Player.INDEX_WALKING_ANIMATION];
		} else if(velocity.x != 0f && Math.abs(velocity.y) <= 0.01f && isSprinting()) {
			updatedCurrentAnimation = animations[Player.INDEX_RUNNING_ANIMATION];
		} else if(velocity.y > 0f) {
			updatedCurrentAnimation = animations[Player.INDEX_JUMP_ANIMATION];
		} else if(velocity.y < 0f) {
			updatedCurrentAnimation = animations[Player.INDEX_FALL_ANIMATION];
		}
		
		if(updatedCurrentAnimation != null && updatedCurrentAnimation != currentAnimation) {
			currentAnimation = updatedCurrentAnimation;
			currentAnimation.start();
		}
	}
	
	@Override
	public void render(SpriteBatch spriteBatch) {
		super.render(spriteBatch);
		
		float widthHealthBar = BAR_WIDTH * health / 100;
		float widthEnergyBar = BAR_WIDTH * energy / 100;
		
		spriteBatch.setColor(Color.BLACK);
		spriteBatch.draw(whiteTexture, position.x * Constants.BOX_TO_WORLD - BAR_WIDTH / 2 - 1, position.y * Constants.BOX_TO_WORLD + SPRITE_HEIGHT/2 + BAR_HEIGHT - 1, BAR_WIDTH + 2, BAR_HEIGHT * 2 + 2);
		
		
		if(health < 30) {
			spriteBatch.setColor(Color.RED);
		} else if(health < 60) {
			spriteBatch.setColor(Color.YELLOW);
		} else {
			spriteBatch.setColor(Color.GREEN);
		}
		spriteBatch.draw(whiteTexture, position.x * Constants.BOX_TO_WORLD - BAR_WIDTH / 2, position.y * Constants.BOX_TO_WORLD + SPRITE_HEIGHT/2 + BAR_HEIGHT * 2, widthHealthBar, BAR_HEIGHT);
		
		spriteBatch.setColor(Color.PINK);
		spriteBatch.draw(whiteTexture, position.x * Constants.BOX_TO_WORLD - BAR_WIDTH / 2, position.y * Constants.BOX_TO_WORLD + SPRITE_HEIGHT/2 + BAR_HEIGHT, widthEnergyBar, BAR_HEIGHT);
		
		spriteBatch.setColor(Color.WHITE);
	}
	
	public static void loadTexture() {
		whiteTexture = new Texture(Gdx.files.internal("data/white_texture.png"));
		textures = new Array<Texture>();
		textures.add(new Texture(Gdx.files.internal("sprites/platformer_sprites_base_white.png")));
		textures.add(new Texture(Gdx.files.internal("sprites/platformer_sprites_base_blue.png")));
		textures.add(new Texture(Gdx.files.internal("sprites/platformer_sprites_base_red.png")));
		textures.add(new Texture(Gdx.files.internal("sprites/platformer_sprites_base_yellow.png")));
	}
	
	public static void dispose() {
		for(Texture t : textures) {
			t.dispose();
		}
		whiteTexture.dispose();
	}
	
	public float getMovingSpeed() {
		return movingSpeed;
	}
	
	public float getMovingSpeedMax() {
		return movingSpeed + movingSpeed * percentAcceleration / 100;
	}
	
	public String getName() {
		return name;
	}
}
